package com.btb.adapter.service;

import static java.util.stream.Collectors.joining;

import java.io.IOException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.web3j.abi.EventEncoder;
import org.web3j.abi.EventValues;
import org.web3j.abi.TypeReference;
import org.web3j.abi.datatypes.Address;
import org.web3j.abi.datatypes.Event;
import org.web3j.abi.datatypes.Uint;
import org.web3j.abi.datatypes.Utf8String;
import org.web3j.abi.datatypes.generated.Uint256;
import org.web3j.abi.datatypes.generated.Uint8;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.core.DefaultBlockParameter;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.Request;
import org.web3j.protocol.core.methods.request.EthFilter;
import org.web3j.protocol.core.methods.response.EthBlock;
import org.web3j.protocol.core.methods.response.EthBlockNumber;
import org.web3j.protocol.core.methods.response.EthGetTransactionReceipt;
import org.web3j.protocol.core.methods.response.EthLog;
import org.web3j.protocol.core.methods.response.Log;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.tx.Contract;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import com.alibaba.fastjson.JSON;
import com.btb.adapter.constant.Constants;
import com.btb.adapter.mapper.TransferRecordMapper;
import com.btb.adapter.model.TransferRecord;
import com.btb.adapter.model.TransferRecordExample;
import com.btb.adapter.utils.Web3jUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class WatchTransferService {

	@Value("${adapter.contract.buy.address}")
	private String contractBuyAddress;

	@Autowired
	private Web3j web3j;

	@Autowired
	private TransferRecordMapper transferRecordMapper;

	Event TRANSFER_EVENT = new Event("Transfer", Arrays.asList(new TypeReference<Address>(true) {
	}, new TypeReference<Address>(true) {
	}, new TypeReference<Uint256>(false) {
	}));
	private final String TRANSFER_EVENT_HASH = EventEncoder.encode(TRANSFER_EVENT);
	// 购买在功事件定义
	Event BUYSUCCESS_EVENT = new Event(Constants.BUY_SUCCESS, Arrays.asList(new TypeReference<Address>() {
	}, new TypeReference<Utf8String>() {
	}, new TypeReference<Uint8>() {
	}, new TypeReference<Uint>() {
	}));
	private final String BUYSUCCESS_EVENT_HASH = EventEncoder.encode(BUYSUCCESS_EVENT);

	@Transactional(rollbackFor = Exception.class)
	public boolean event(String blockNumber) {
		boolean result = true;
		try {
			log.error("event =========================> {}", blockNumber);
			EthFilter filter = new EthFilter(DefaultBlockParameter.valueOf(new BigInteger(blockNumber)),
					DefaultBlockParameter.valueOf(new BigInteger(blockNumber)), contractBuyAddress)
							.addSingleTopic(BUYSUCCESS_EVENT_HASH);
			contractExtract(filter);
		} catch (Exception e) {
			result = false;
		}
		return result;
	}

	@Transactional(rollbackFor = Exception.class)
	public boolean event(String startBlockNumber, String endBlockNumber) {
		boolean result = true;
		try {
			EthFilter filter = new EthFilter(DefaultBlockParameter.valueOf(new BigInteger(startBlockNumber)),
					DefaultBlockParameter.valueOf(new BigInteger(endBlockNumber)), contractBuyAddress)
							.addSingleTopic(BUYSUCCESS_EVENT_HASH);
			contractExtract(filter);
		} catch (Exception e) {
			result = false;
		}
		return result;
	}

	private void contractExtract(EthFilter filter) {
		EthLog ethLog = null;
		try {
			ethLog = web3j.ethGetLogs(filter).send();
		} catch (Exception e) {
			log.error("contractExtract err: {}", e.getMessage(), e);
			throw new IllegalStateException(e.getMessage());
		}
		if (Objects.isNull(ethLog)) {
			return;
		}

		ethLog.getLogs().forEach(r -> {
			Log logs = (Log) r;
			TransferRecord query = query(logs);
			if(Objects.nonNull(query)) {
				TransferRecord selectOne = selectOne(logs.getTransactionHash());
				if (Objects.nonNull(selectOne)) {
					// 说明旧的数据状态变化了 用新的状态更新旧的
					if (!selectOne.isOk() && query.isOk()) {
						updateStatus(selectOne, query);
					}
				} else {
					transferRecordMapper.insert(query);
				}
			}
		});
	}

	private TransferRecord query(final Log logs) {
		Request<?, EthGetTransactionReceipt> ethGetTransactionReceipt = web3j
				.ethGetTransactionReceipt(logs.getTransactionHash());
		try {
			EthGetTransactionReceipt send = ethGetTransactionReceipt.send();
			if(Objects.isNull(send)) {
				return null;
			}
			TransactionReceipt result = send.getResult();
			return build(logs, result);
		} catch (Exception e) {
			log.error("TransferRecord query err: {}", e.getMessage(), e);
			throw new IllegalStateException(e.getMessage());
		}
	}

	private String queryTimestamp(BigInteger blockNumber) {
		String timestamp = "";
		try {
			EthBlock ethBlock = web3j.ethGetBlockByNumber(DefaultBlockParameter.valueOf(blockNumber), false).send();
			timestamp = String.valueOf(ethBlock.getBlock().getTimestamp());
		} catch (Exception e) {
			log.error("Block timestamp get failure,block number is {}", blockNumber);
			log.error("Block timestamp get failure,{}", e);
		}
		return timestamp;
	}

	private TransferRecord build(final Log logs, TransactionReceipt result) {
		String timestamp = queryTimestamp(result.getBlockNumber());
		Long time = Long.parseLong(timestamp) * 1000;
		TransferRecord build = TransferRecord.builder().blockNumber(result.getBlockNumber().longValue())
				.transactionHash(result.getTransactionHash()).fromAddress(result.getFrom()).toAddress(result.getTo())
				.data(logs.getData())
				.notifyNum(0)  // 初始的值是0
//			.value(new BigDecimal(Web3jUtils.fmt(logs.getData())))
				.status(String.valueOf(result.isStatusOK()))
				.gasUsed(new BigDecimal(Web3jUtils.fmt(result.getGasUsed()))).timestamp(String.valueOf(time))
				.createTime(new Date()).updatedTime(new Date()).build();
		EventValues eventValues = Contract.staticExtractEventParameters(BUYSUCCESS_EVENT, logs);
//		String from = (String) eventValues.getNonIndexedValues().get(0).getValue();
		String orderId = (String) eventValues.getNonIndexedValues().get(1).getValue();
		BigInteger type = (BigInteger) eventValues.getNonIndexedValues().get(2).getValue();
		BigInteger orderTotalPrice = (BigInteger) eventValues.getNonIndexedValues().get(3).getValue();
		build.setPayOrderId(orderId);
		build.setPayType(type.toString());
		build.setPayOrderPrice(new BigDecimal(Web3jUtils.fmt(orderTotalPrice)));
		return build;
	}

	private TransferRecord selectOne(final String transactionHash) {
		TransferRecordExample example = new TransferRecordExample();
		example.createCriteria().andTransactionHashEqualTo(transactionHash);
		List<TransferRecord> selectByExample = transferRecordMapper.selectByExample(example);
		return selectByExample.stream().findAny().orElse(null);
	}

	private int updateStatus(final TransferRecord oldR, final TransferRecord newR) {
		TransferRecord record = TransferRecord.builder().id(oldR.getId()).status(newR.getStatus()).build();
		return transferRecordMapper.updateByPrimaryKeySelective(record);
	}

	/**
	 * 区块高度
	 */
	public BigInteger getBlockNumber() {
		EthBlockNumber send;
		try {
			send = web3j.ethBlockNumber().send();
			return send.getBlockNumber();
		} catch (IOException e) {
			log.warn("getBlockNumber err:{}", e.getMessage(), e);
		}
		return null;
	}
}
